<?php
// +----------------------------------------------------------------------
// | sfadmin - Suifeng Administration
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://suifeng.tech All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: suifeng <1787585212@qq.com>
// +----------------------------------------------------------------------

namespace suifeng\builder;

use suifeng\builder\form\Control;
use suifeng\builder\form\Layout;
use suifeng\helper\StringHelper;
use suifeng\helper\HtmlHelper;
use suifeng\builder\form\control\Hidden;
use suifeng\helper\BaseObject;

class FormBuilder extends BaseObject
{
    /**
     * 模型对象。
     *
     * @var unknown
     */
    public $model;

    /**
     * 表单名称。
     *
     * @var unknown
     */
    public $name;

    /**
     * 提交地址。
     *
     * @var unknown
     */
    public $action;

    /**
     * 提交方法。
     *
     * @var string
     */
    public $method;

    /**
     * 布局对象。
     *
     * @var unknown
     */
    public $layout;

    /**
     * 控件数组。
     *
     * @var array
     */
    public $controls = [];

    /**
     * 是否有上传控件？
     *
     * @var boolean
     */
    public $has_upload = false;

    /**
     * 模型对象。
     *
     * @var unknown
     */
    public $model_object;

    /**
     * 主KEY字段名称，Unique验证器要用到。
     *
     * @var unknown
     */
    public $key_field;

    /**
     * 主KEY字段值，Unique验证器要用到。
     *
     * @var unknown
     */
    public $key_value;

    /**
     * 数据类型对应控件类型。
     *
     * @var array
     */
    protected $data_type_map = array(
        'int'   => 'number',
    );

    /**
     * 不可以修改的字段列表。
     *
     * @var array
     */
    protected $uneditable_field_names = array(
        'id', 'create_time', 'update_time',
    );

//     /**
//      * 原始数据。
//      *
//      * @var array
//      */
//     public $original_data = array();

//     /**
//      * 提交的数据。
//      *
//      * @var array
//      */
//     public $submitted_data = array();

    /**
     * 接受的的数据。
     *
     * @var array
     */
    public $acceptted_data = array();

    /**
     * 错误信息。
     *
     * @var array
     */
    public $errors = array();

    /**
     * 数组名称。
     * 表单控件可以使用config[name]这种格式。
     *
     * @var unknown
     */
    public $array_name;

    /**
     * 提交按钮。
     *
     * @var array
     */
    public $submit_button;

    /**
     * 取消按钮。
     *
     * @var array
     */
    public $cancel_button;

    protected $_run_state = array(
        'initialized'   => false,
        'validated'     => false,
    );

    /**
     * 构造方法。
     *
     * @param array $config
     */
    public function __construct($model = null, $config = array())
    {
        parent::__construct($config);

        $this->model = $model;

        if (empty($this->name)) {
            $this->name = StringHelper::unique_name('sf-form');
        }

        if (empty($this->method)) {
            $this->method = 'POST';
        } else {
            $this->method = strtoupper($this->method);
        }

        if (empty($this->action)) {
            $this->action = request()->url();
        }

        if (empty($this->attributes['class'])) {
            $this->attributes['class'] = 'form';
        }

        if (empty($this->submit_button)) {
            $this->submit_button = HtmlBuilder::submit();
        }

        if (empty($this->cancel_button)) {
            $this->cancel_button = HtmlBuilder::cancel();
        }
    }

//     /**
//      * 初始化默认属性。
//      */
//     protected function _init_default()
//     {
//         if (empty($this->name)) {
//             $this->name = StrUtil::unique_name('sf-form');
//         }

//         if (empty($this->method)) {
//             $this->method = 'POST';
//         } else {
//             $this->method = strtoupper($this->method);
//         }

//         if (empty($this->action)) {
//             $this->action = request()->url();
//         }

//         if (empty($this->attributes['class'])) {
//             $this->attributes['class'] = 'form-horizontal form form-builder';
//         }

//         if (empty($this->submit_button)) {
//             $this->submit_button = new SimpleTag(array(
//                 'tag'       =>  'a',
//                 'attributes'=>  [
//                     'type'      =>  'submit',
//                     'class'     =>  'btn btn-primary submit ajax-post',
//                     'target-form'=> 'form-builder',
//                 ],
//                 'content'   =>  ['确定'],
//             ));
//         }

//         if (empty($this->cancel_button)) {
//             $this->cancel_button = new SimpleTag(array(
//                 'tag'       =>  'a',
//                 'attributes'=>  [
//                     'class'     =>  'btn btn-danger return',
//                     'onclick'   =>  'javascript:history.back(-1);return false;',
//                 ],
//                 'content'   =>  ['取消'],
//             ));
//         }
//     }

//     /**
//      * 初始化控件。
//      */
//     protected function _init_controls()
//     {
//         if ($this->model) {
//             $model_fields = $this->model->model_fields;
//         } else {
//             $model_fields = [];
//         }

//         // 根据$model构建$control
//         if (!$this->controls && $model_fields) {
//             $form_fields = $this->model->form_fields;

//             if (empty($form_fields)) {
//                 $form_fields = array();

//                 foreach ($model_fields as $field_name => $field_info) {
//                     if (isset($field_info['identity']) && $field_info['identity']) {
//                         continue;
//                     } else if (in_array($field_name, $this->uneditable_field_names)) {
//                         continue;
//                     } else {
//                         $form_fields[] = $field_name;
//                     }
//                 }
//             }

//             $this->controls = $form_fields;
//         }

//         // 转换$controls中的string和array为control
//         $controls = $this->controls; $this->controls = [];
//         foreach ($controls as $name => $control) {
//             if (is_string($control)) {
//                 $name = $control;
//                 $control = [];
//             } else if (is_array($control)) {
//                 if (is_int($name)) {
//                     if (isset($control['name'])) {
//                         $name = $control['name'];
//                     } else if (isset($control['field'])) {
//                         $name = $control['field'];
//                     } else {
//                         continue; // 找不到$name
//                     }
//                 }
//             }

//             if (is_array($control)) {
//                 if (isset($control['control'])) {
//                     $type = $control['control'];
//                 } else if (isset($control['display'])) {
//                     $type = $control['display'];
//                 } else {
//                     $field_info = isset($model_fields[$name]) ? $model_fields[$name] : [];

//                     if (isset($field_info['control'])) {
//                         $type = $field_info['control'];
//                     } else if (isset($field_info['display'])) {
//                         $type = $field_info['display'];
//                     } else if (isset($field_info['dbtype']) && array_key_exists($field_info['dbtype'], $this->data_type_map)) {
//                         $type = $this->data_type_map[$field_info['dbtype']];
//                     } else {
//                         $type = 'text';
//                     }
//                 }

//                 $control = Control::create($type, $name, $control);
//             }

//             $control->form =& $this;
//             $control->initialize();
//             $this->controls[$name] = $control;
//         }
//     }


    /**
     * 添加控件对象。
     *
     * @param unknown $type
     * @param unknown $name
     * @param array $config
     * @return mixed
     */
    public function add_control($name, $type = null, $label = null, $config = [])
    {
        if (empty($type)) {
            $type = 'text';
        }

        $class = '\\suifeng\\builder\\form\\control\\' . implode('', array_map('ucfirst', explode('_', $type)));
        if (!class_exists($class)) {
            throw new \Exception('Control type not exists: ' . $type);
        }

        $config['form'] = $this;

        if (is_array($label)) {
            $config = array_merge($config, $label);
        } else {
            $label && $config['label'] = $label;
        }

        $control = new $class($name, $config);
        $this->controls[$name] = $control;
        return $control;
    }











//     /**
//      * 初始化控件列表。
//      *
//      * @param unknown $config
//      */
//     protected function _init_controls($config)
//     {
//         $this->controls = array();
//         $this->has_upload = false;

//         $model_fields = $this->model ? $this->model->model_fields : array();

//         foreach ($config as $control_config) {
//             if (isset($control_config['name'])) {
//                 $control_name = $control_config['name'];
//             } else if (isset($control_config['field'])) {
//                 $control_name = $control_config['field'];
//                 unset($control_config['field']);
//                 $control_config['name'] = $control_name;
//             } else {
//                 continue; //skip
//             }

//             if (! isset($control_config['control'])) {
//                 if (array_key_exists($model_fields[$control_name]['dbtype'], $this->data_type_map)) {
//                     $control_config['control'] = $this->data_type_map[$model_fields[$control_name]['dbtype']];
//                 } else {
//                     $control_config['control'] = 'text';
//                 }
//             }

//             $obj = Control::create($control_config['control'], $control_name, $control_config);
//             $obj->form =& $this;
//             $obj->parent =& $this;
//             $obj->initialize();
//             $this->controls[$control_name] = $obj;

//             if ($obj instanceof \suifeng\form\controls\File) {
//                 $this->has_upload = true;
//             }
//         }
//     }

//     /**
//      * 从模型对象中解析控件。
//      *
//      * @return unknown[]
//      */
//     protected function _parse_controls_from_model()
//     {
//         $model_fields = $this->model->model_fields;
//         $form_fields = $this->model->form_fields;

//         // 默认字段
//         if (! $form_fields) {
//             $form_fields = array();

//             foreach (array_keys($model_fields) as $field_name) {
//                 if (isset($model_fields[$field_name]['identity']) && $model_fields[$field_name]['identity']) {
//                     continue;
//                 } else if (in_array($field_name, $this->uneditable_field_names)) {
//                     continue;
//                 } else {
//                     $form_fields[] = $field_name;
//                 }
//             }
//         }

//         $result_list = array();

//         foreach ($form_fields as $key => $val) {
//             if (is_numeric($key)) {
//                 if (is_array($val)) {
//                     $control_config = $val;
//                     $field_name = isset($control_config['field']) ? $control_config['field'] : null;
//                 } else {
//                     $field_name = $val;
//                     $control_config = array();
//                 }
//             } else {
//                 $field_name = $key;
//                 $control_config = $val;
//             }

//             // 无效字段
//             if (! array_key_exists($field_name, $model_fields)) {
//                 continue;
//             }

//             $control_config['name'] = $field_name;
//             $control_config['field'] = $field_name;

//             // 字段信息
//             $control_config['field_info'] = $model_fields[$field_name];
//             $control_config['field_info']['name'] = $field_name;



//             $control_config = array_merge_recursive($model_fields[$field_name], $control_config);

// //             // 合并字段定义
// //             if (isset($model_fields[$field_name]['control']) &&
// //                 is_array($model_fields[$field_name]['control'])) {
// //                 $control_config = array_merge($model_fields[$field_name]['control'], $control_config);
// //             }

// //             // 显示类型
// //             if (! isset($control_config['control'])) {
// //                 if (isset($model_fields[$field_name]['control']) && is_string($model_fields[$field_name]['control'])) {
// //                     $control_config['control'] = $model_fields[$field_name]['control'];
// //                 }
// //             }

// //             // 显示标题
// //             if (! isset($control_config['label'])) {
// //                 if (isset($model_fields[$field_name]['title'])) {
// //                     $control_config['label'] = $model_fields[$field_name]['title'];
// //                 } else {
// //                     $control_config['label'] = \suifeng\helper\StrUtil::friendly_text($field_name);
// //                 }
// //             }

// //             // 显示标题
// //             if (! isset($control_config['help'])) {
// //                 if (isset($model_fields[$field_name]['help'])) {
// //                     $control_config['help'] = $model_fields[$field_name]['help'];
// //                 }
// //             }

// //             // 验证规则
// //             if (! isset($control_config['rules'])) {
// //                 if (isset($model_fields[$field_name]['rules'])) {
// //                     $control_config['rules'] = $model_fields[$field_name]['rules'];
// //                 }
// //             }

// //             // 数据类型
// //             $control_config['data_type'] = $model_fields[$field_name]['type'];

//             // 显示标题
//             if (! isset($control_config['label'])) {
//                 if (isset($model_fields[$field_name]['title'])) {
//                     $control_config['label'] = $model_fields[$field_name]['title'];
//                 } else {
//                     $control_config['label'] = \suifeng\helper\StrUtil::friendly_text($field_name);
//                 }
//             }

//             $result_list[] = $control_config;
//         }

//         return $result_list;
//     }

//     /**
//      * 初始化布局对象。
//      *
//      * @param unknown $config
//      */
//     protected function _init_layout($config)
//     {
//         if ($config instanceof Layout) {
//             $this->layout = $config;
//         } else {
//             $this->layout = $this->_create_layout_obj($this, $config);
//         }
//     }

//     /**
//      * 从模型对象中解析布局。
//      *
//      * @return unknown[]
//      */
//     protected function _parse_layout_from_model()
//     {
//         $form_layout = $this->model->form_fields;

//         if (! $form_layout) {
//             $form_layout = $this->_build_default_layout();
//         }

//         return $form_layout;
//     }

    /**
     * 输出HTML。
     *
     * @param string $template
     * @param string $variables
     * @return string
     */
    public function render_html()
    {
        $this->_accept_submitted_data();

//         foreach ($this->controls as $control) {
//             $control->form =& $this;
//             $control->initialize();
//         }

        if (empty($this->layout)) {
            $this->layout = $this->_build_default_layout();
        }

        if (is_array($this->layout)) {
            $this->layout = $this->_create_layout_obj($this, $this->layout);
        }

        // 先输出布局对象，这样可以让布局对象修改一些Form的属性。
        $layout_output = $this->layout ? $this->layout->render_html() : '';

        $form_attributes = $this->attributes;
        $form_attributes['id'] = $this->name;
        $form_attributes['name'] = $this->name;
        $form_attributes['method'] = strtoupper($this->method);
        $form_attributes['action'] = $this->action;

        if ($this->has_upload) {
            $form_attributes['enctype'] = 'multipart/form-data';
        }

        HtmlHelper::add_class($form_attributes, 'sf-form');

        $output = HtmlHelper::render_start_tag('form', $form_attributes);

        $output .= $layout_output;
        $output .= $this->_render_bottom_buttons();
        $output .= $this->_render_hidden_controls();

        $output .= HtmlHelper::render_end_tag('form');

        $focus_control = $this->find_first_control();
        if ($focus_control) {
            $output .= <<<EOF
<script type="text/javascript">
    $(function(){
        $('#{$this->name} [name={$focus_control->name}]').focus();
    });
</script>
EOF;
        }

        return $output;
    }

    /**
     * 构建默认的布局。
     *
     * @return NULL
     */
    protected function _build_default_layout()
    {
        $result_list = array();

        foreach ($this->controls as $control) {
            if (! ($control instanceof \suifeng\builder\form\control\Hidden)) {
                $result_list[] = $control->name;
            }
        }

        return $result_list;
    }

    /**
     * 创建布局对象。
     *
     * @param array $config
     * @throws \Exception
     * @return unknown
     */
    protected function _create_layout_obj($parent, $config = array())
    {
        if (isset($config['layout'])) {
            $layout_type = $config['layout'];
            unset($config['layout']);
        } else {
            // 默认布局
            $layout_type = 'vertical';
        }

        $layout_obj = Layout::factory($layout_type);
        $layout_obj->form = $this;
        $layout_obj->parent = $parent;

        $layout_config = array();

        if (isset($config['content'])) {
            $layout_config['content'] = $config['content'];
        } else {
            $layout_config['content'] = array();
        }

        foreach ($config as $key => $val) {
            if (is_numeric($key)) {
                if (is_array($val)) {
                    $layout_config['content'][] = $this->_create_layout_obj($layout_obj, $val);
                } else {
                    if (isset($this->controls[$val])) {
                        $layout_config['content'][] = $this->controls[$val];
                    }
                }
            } else {
                $layout_config[$key] = $val;
            }
        }

        $layout_obj->merge_config($layout_config);

        return $layout_obj;
    }

    /**
     * 输出底部按钮。
     *
     * @return string
     */
    protected function _render_bottom_buttons()
    {
        $output = '<div class="form-group"></div>' . //加一空行
            '<div class="form-group bottom_button_list">' .
            $this->submit_button->render_html() .
            '&nbsp;' .
            $this->cancel_button->render_html() .
            '</div>';
            return $output;
    }

    /**
     * 输出隐藏字段。
     *
     * @return string
     */
    protected function _render_hidden_controls()
    {
        $output = '';
        foreach ($this->controls as $control) {
            if ($control instanceof \suifeng\builder\form\control\Hidden) {
                $output .= $control->render_html();
            }
        }
        return $output;
    }

//     /**
//      * 初始化提交数据。
//      *
//      * @param unknown $array
//      */
//     public function init_submitted_data($array = null)
//     {
//         if ($array === null) {
//             global ${'_' . $this->method};
//             $array = ${'_' . $this->method};
//         }

//         $this->submitted_data = array();

//         if ($this->controls) {
//             foreach (array_keys($this->controls) as $name) {
//                 $this->submitted_data[$name] = $this->controls[$name]->get_submitted_value($array);
//             }
//         }
//     }

//     /**
//      * 验证表单控件。
//      *
//      * @return boolean
//      */
//     public function validate()
//     {
//         $this->is_valid = true;

//         foreach (array_keys($this->controls) as $name) {
//             if (! $this->controls[$name]->is_valid()) {
//                 $this->is_valid = false;
//             }
//         }

//         return $this->is_valid;
//     }



    /**
     * 查找第一个可用控件，用来定位输入焦点。
     *
     * @return \suifeng\builder\form\control\Hidden|NULL
     */
    public function find_first_control()
    {
        foreach ($this->controls as $name => $control) {
            if (! ($control instanceof Hidden)) {
                return $control;
            }
        }
        return null;
    }

    /**
     * 初始化表单数据。
     */
    public function initialize($data = null)
    {
        if ($data === null) {
            if (strtoupper($this->method) == 'POST') {
                $data = request()->post();
            } else if (strtoupper($this->method) == 'GET') {
                $data = request()->get();
            } else {
                $data = request()->param();
            }

            if ($this->array_name) {
                $data = isset($data[$this->array_name]) ? (array) $data[$this->array_name] : array();
            }
        }

        foreach ($this->controls as $control) {
            $control->parse_from_array($data);
        }

        $this->_run_state['initialized'] = true;
    }

    /**
     * 接收提交的数据，如果已经初始化initialize，不要重复执行。
     */
    protected function _accept_submitted_data()
    {
        if ($this->_run_state['initialized']) {
            return;
        }

        $this->initialize();
    }

    /**
     * 验证表单控件。
     *
     * @return boolean
     */
    public function validate()
    {
        $this->_accept_submitted_data();

        $is_valid = true;
        $this->errors = array();
        $this->acceptted_data = array();

        foreach ($this->controls as $name => $control) {
            if ($control->validate()) {
                $this->acceptted_data[$name] = $control->value;
            } else{
                $is_valid = false;
                $this->acceptted_data[$name] = null;
            }
        }

        $this->_run_state['validated'] = true;
        return $is_valid;
    }

    /**
     * 表单是否有效?
     *
     * @return boolean
     */
    public function is_valid()
    {
        if (!$this->_run_state['validated']) {
            $this->validate();
        }

        if (!empty($this->errors)) {
            return false;
        }

        foreach ($this->controls as $control) {
            if (!$control->is_valid()) {
                return false;
            }
        }

        return true;
    }

    /**
     * 支持的控件类型。
     *
     * @return string[]
     */
    public static function support_controls()
    {
        $list = [];

        foreach (glob(__DIR__ . '/form/control/*.php') as $file) {
            $list[] = strtolower(basename($file, '.php'));
        }

        return $list;
    }
















    //     /**
    //      * 解析布局。
    //      */
    //     protected function _parse_form_layout($array)
    //     {
    //         if (isset($array['layout'])) {
    //             $type = $array['layout'];
    //             unset($array['layout']);
    //         } else {
    //             // 默认布局
    //             $type = 'vertical';
    //         }

    //         $class_name = "\\suifeng\\layout\\" . ucfirst($type);
    //         if (! class_exists($class_name)) {
    //             throw new \Exception("layout type not exists: " . $type);
    //         }

    //         $layout = new $class_name();
    //         $layout->form_obj = & $this;
    //         $layout->form_properties = & $this->form_properties;

    //         foreach ($array as $key => $val) {
    //             if (is_numeric($key)) {
    //                 if (is_array($val)) {
    //                     $layout->content[] = $this->_parse_form_layout($val);
    //                 } else {
    //                     if (isset($this->controls[$val])) {
    //                         $layout->content[] = $this->controls[$val];
    //                     }
    //                 }
    //             } else {
    //                 $layout->attributes[$key] = $val;
    //             }
    //         }

    //         return $layout;
    //     }

    //     /**
    //      * 输出底部按钮。
    //      *
    //      * @return string
    //      */
    //     protected function _render_bottom_buttons()
    //     {
    //         $output = '<div class="form-group"></div>' . //加一空行
    //             '<div class="form-group bottom_button_list">' .
    //             '<a class="btn btn-primary submit ajax-post" type="submit" target-form="' . $this->form_properties['name'] . '">确定</a>' .
    //             '&nbsp;' .
    //             '<a class="btn btn-danger return" onclick="javascript:history.back(-1);return false;">取消</a>' .
    //             '</div>';
    //         return $output;
    //     }








    //     /**
    //      * 获取控件HTML。
    //      *
    //      * @param unknown $control
    //      * @return unknown
    //      */
    //     protected function get_control_html($control)
    //     {
    //         return $control->toHTML();
    //     }

    //     /**
    //      * 动态调用。
    //      *
    //      * @param unknown $name
    //      * @param unknown $args
    //      */
    //     public function __call($name, $args)
    //     {
    //         if (count($args) == 0) {
    //             return isset($this->form_properties[$name]) ? $this->form_properties[$name] : null;
    //         } else if (count($args) == 1) {
    //             $this->form_properties[$name] = $args[0];
    //             return $this;
    //         } else {
    //             $this->form_properties[$name] = $args;
    //             return $this;
    //         }
    //     }


}
